<?php

namespace App\Models;

use App\Exceptions\AppException;
use Carbon\Carbon;
use Dcat\Admin\Traits\HasDateTimeFormatter;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;

/**
 * Class Alarm
 * @package App\Models
 * @property Carbon $time
 */
class Alarm extends Model
{
    use HasDateTimeFormatter;

    const STATUS_WARN = '报警';
    const STATUS_RECOVER = '恢复';

    protected $fillable = ['channel_id', 'station', 'source', 'type', 'duration', 'time', 'end_time', 'image', 'description', 'frequency', 'reason', 'status'];
    protected $casts = ['time' => 'datetime', 'end_time' => 'datetime'];

    public function channel()
    {
        return $this->belongsTo(Channel::class);
    }

    /**
     * 设置持续时间
     * @param Carbon $carbon
     * @created 2020/6/1
     */
    public function setDuration(Carbon $carbon): void
    {
        $duration = $carbon->diffInSeconds($this->time);
        $this->update(['duration' => $duration]);
    }


    /**
     * @return void
     */
    public function selfRecovery()
    {
        $carbon = Carbon::now();
        $duration = $carbon->diffInSeconds($this->time);
        $this->update([
            'duration' => $duration,
            'status' => Alarm::STATUS_RECOVER,
            'end_time' => $carbon
        ]);
    }

    /**
     * 接收报警
     * @param $attributes
     * @return bool|Alarm
     * @throws AppException
     */
    public static function receive($attributes): bool|Alarm
    {
        /**
         * 验证字段
         */
        $validator = Validator::make($attributes, [
            'Source' => 'required|string',
            'Type' => 'required|string',
            'ImagePosition' => 'required|string',
            'Description' => 'required|string',
            'Frequency' => 'required|string',
            'Reason' => 'required|string',
            'Status' => ['required', Rule::in(['报警', '恢复'])],
        ]);
        if ($validator->fails()) {
            throw new AppException($validator->errors()->first());
        }

        $data = [
            'station' => Arr::get($attributes, 'Station'),
            'source' => Arr::get($attributes, 'Source'),
            'type' => Arr::get($attributes, 'Type'),
            'duration' => 0,
//            'time' => Carbon::parse(Arr::get($attributes, 'Time')),
            'time' => Carbon::now(),
            'image' => Arr::get($attributes, 'ImagePosition'),
            'description' => Arr::get($attributes, 'Description'),
            'frequency' => Arr::get($attributes, 'Frequency'),
            'reason' => Arr::get($attributes, 'Reason'),
            'status' => Arr::get($attributes, 'Status'),
        ];

        /**
         * 确认频率
         * @var Channel $channel
         */
        $channel = Channel::where('frequency', $data['frequency'])->first();
        if (empty($channel)) {
            throw new AppException("频率“{$data['frequency']}”不存在");
        }

        // 确认时间
        if (!self::isInEffectiveTime($channel)) {
            throw new AppException("频率“{$data['frequency']}”不在接收有效时间段内");
        }

        switch ($data['status']) {
            case Alarm::STATUS_WARN:
                return self::warn($channel, $data);
            case Alarm::STATUS_RECOVER:
                return self::recovery($channel, $data);
            default:
                info('无效的状态|' . json_encode($attributes));
                break;
        }

        return false;
    }

    /**
     * 报警
     * @param $channel
     * @param $data
     * @return bool
     */
    public static function warn($channel, $data): bool
    {
        /**
         * @var Alarm $alarm
         */
        $alarm = Alarm::where([
            'channel_id' => $channel->id,
            'status' => Alarm::STATUS_WARN,
        ])->first();

        // 当前频率存在报警
        if ($alarm) {
            $alarm->setDuration($data['time']);
            return false;
        } // 不存在，则创建
        else {
            Alarm::create(array_merge([
                'channel_id' => $channel->id,
            ], $data));
            return true;
        }
    }

    /**
     * 恢复
     * @param $channel
     * @param $data
     * @return bool
     */
    public static function recovery($channel, $data): bool
    {
        $alarmList = Alarm::where([
            'channel_id' => $channel->id, 'status' => Alarm::STATUS_WARN
        ])->get();
        foreach ($alarmList as $aItem) {
            /**
             * @var Alarm $aItem
             */
            $aItem->selfRecovery();
        }

        return true;
    }

    /**
     * 判断频率是否在有效时间内
     * @param Channel $channel
     * @return bool
     */
    public static function isInEffectiveTime(Channel $channel): bool
    {
        // 周几
        $dayOfWeekNow = Carbon::now()->dayOfWeek;

        // 有效日期
        $channelPeriods = ChannelPeriod::where([
            'channel_id' => $channel->id,
            'day_of_week' => $dayOfWeekNow,
            'status' => ChannelPeriod::STATUS_ENABLE,
        ])->get();
        if (count($channelPeriods) == 0) {
            return false;
        }

        // 有效时间段
        foreach ($channelPeriods as $item) {
            // 在时间范围内
            $s = Carbon::createFromFormat('H:i:s', $item->started_at);
            $e = Carbon::createFromFormat('H:i:s', $item->ended_at);

            $n = time();
            if ($s->timestamp <= $n && $e->timestamp >= $n) {
                return true;
            }
        }

        return false;
    }

    /**
     * changeTimeType
     * @param $seconds
     * @return string
     * @created 2020/6/2
     */
    public static function formatSeconds($seconds)
    {
        $time = $seconds;
        if (is_numeric($time)) {
            $value = [
                "days" => 0,
                "hours" => 0,
                "minutes" => 0,
//                "seconds" => 0,
            ];
            $keyMap = [
                'days' => '天',
                'hours' => '小时',
                'minutes' => '分',
                'seconds' => '秒',
            ];
            if ($time >= 86400) {
                $value["days"] = floor($time / 86400);
                $time = ($time % 86400);
            }
            if ($time >= 3600) {
                $value["hours"] = floor($time / 3600);
                $time = ($time % 3600);
            }
            if ($time >= 60) {
                $value["minutes"] = floor($time / 60);
                $time = ($time % 60);
            }
            $value["seconds"] = floor($time);
            //return (array) $value;
            $timeView = '';
            foreach ($value as $key => $item) {
                if ($item > 0) {
                    $timeView .= $item . $keyMap[$key];
                }
            }
            return $timeView;
        }

        return '';
    }
}
